/*
 * @Author: wwssaabb
 * @Date: 2021-12-04 16:00:46
 * @LastEditTime: 2021-12-07 15:38:27
 * @FilePath: \handwritten-code\promise\promise.js
 */

//手写promise A+

//初始化状态
const PENDING = 'pending' //等待
const FULFILLED = 'fulfilled' //完成
const REJECTED = 'rejected' //失败

class MyPromise {
  constructor(executor) {
    this.initState() //初始化状态
    this.initBind() //初始化this
    //传进来的执行器函数,执行错误的时候改变状态为失败并返回error
    try {
      executor(this.resolve, this.reject)
    } catch (error) {
      this.reject(error)
    }
  }
  initState() {
    this.value = null //成功结果
    this.reason = null //失败的原因
    this.state = PENDING //初始的状态
    //成功时回调队列
    this.onFulfilledCallback = []
    //失败时回调队列
    this.onRejectedCallback = []
  }
  initBind() {
    //初始化this，永远指向当前的实例
    this.resolve = this.resolve.bind(this)
    this.reject = this.reject.bind(this)
  }

  //静态方法resolve
  static resolve(value) {
    //如果是MyPromise的实例则返回，不是则返回MyPromise的实例
    if (value instanceof MyPromise) return value
    return new MyPromise(resolve => resolve(value))
  }

  resolve(value) {
    //调用resolve会将状态修改为成功，只有是等待状态下才会改变
    if (this.state === PENDING) {
      this.state = FULFILLED
      this.value = value
      //循环取出回调进行执行
      while (this.onFulfilledCallback.length) {
        this.onFulfilledCallback.shift()(value)
      }
    }
  }

  //静态reject方法
  static reject(value) {
    //如果是MyPromise的实例则返回，不是则返回MyPromise的实例
    if (value instanceof MyPromise) return value
    return new MyPromise((_, reject) => reject(value))
  }

  reject(reason) {
    //调用reject会将状态修改为失败，只有是等待状态下才会改变
    if (this.state === PENDING) {
      this.state = REJECTED
      this.reason = reason
      //循环取出回调进行执行
      while (this.onRejectedCallback.length) {
        this.onRejectedCallback.shift()(reason)
      }
    }
  }

  //.then回调
  then(onFulfilled, onRejected) {
    //如果不传则使用默认函数
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
    onRejected = typeof onFulfilled === 'function' ? onRejected : reason => {
      throw reason
    }

    //then调用的时候会注册一个微任务，使用queueMicrotask模拟微任务
    const self = new MyPromise((resolve, reject) => {
      const fulfilledMicrotask = () => {
        queueMicrotask(() => {
          try {
            resolvePromise(onFulfilled(this.value), self, resolve, reject)
          } catch (error) {
            reject(error)
          }
        })
      }
      const rejectedMicrotask = () => {
        queueMicrotask(() => {
          try {
            resolvePromise(onRejected(this.reason), self, resolve, reject)
          } catch (error) {
            reject(error)
          }
        })
      }

      if (this.state === FULFILLED) fulfilledMicrotask()
      if (this.state === REJECTED) rejectedMicrotask()
      if (this.state === PENDING) {
        //如果是等待状态，则将两个回调任务缓存
        this.onFulfilledCallback.push(fulfilledMicrotask)
        this.onRejectedCallback.push(rejectedMicrotask)
      }
    })
    return self
  }

  catch (onRejected) {
    //只需要进行错误处理
    this.then(undefined, onRejected)
  }
}


function resolvePromise(instance, self, resolve, reject) {
  //如果是自己的情况则抛出类型错误
  if (instance === self) return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))

  if (typeof instance === 'object' || typeof instance === 'function') {
    //如果是为null的情况
    if (instance === null) return reject(instance)

    let then;
    try {
      //或取then
      then = instance.then
    } catch (error) {
      //获取then报错时reject
      return reject(error)
    }

    if (typeof then === 'function') { //如果then是一个函数

      //标记为是否执行的状态，忽略多余调用执行
      let called = false

      try {
        then.call(instance, x => { //resolve
            if (called) return
            called = true
            resolvePromise(instance, x, resolve, reject)
          },
          y => { //reject
            if (called) return
            called = true
            resolvePromise(instance, y, resolve, reject)
          })
      } catch (error) {
        if (called) return
        reject(error)
      }
    } else {
      resolve(instance)
    }
    resolve(instance)
  }

  //判断是否为MyPromise的实例，是则调用then
  if (instance instanceof MyPromise) {
    this.then(resolve, reject)
  } else {
    resolve(instance)
  }
}